#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pd_linux.h"
#include "config_ftdi.h"
#include "libusb.h"
#include "xlibusb.h"

namespace config_ftdi
{
    const int FTDI_SIO_RESET = 0;
    const int FTDI_SIO_MODEM_CTRL = 1;
    const int FTDI_SIO_SET_FLOW_CTRL = 2;
    const int FTDI_SIO_SET_BAUD_RATE = 3;
    const int FTDI_SIO_SET_DATA = 4;

    const int FTDI_REQTYPE_HOST2DEVICE = 0x40;

    /**
     *  RTS and DTR values obtained from FreeBSD FTDI driver
     *  https://github.com/freebsd/freebsd/blob/70b396ca9c54a94c3fad73c3ceb0a76dffbde635/sys/dev/usb/serial/uftdi_reg.h
     */
    const int FTDI_SIO_SET_DTR_MASK = 0x1;
    const int FTDI_SIO_SET_DTR_HIGH = (1 | (FTDI_SIO_SET_DTR_MASK << 8));
    const int FTDI_SIO_SET_DTR_LOW = (0 | (FTDI_SIO_SET_DTR_MASK << 8));
    const int FTDI_SIO_SET_RTS_MASK = 0x2;
    const int FTDI_SIO_SET_RTS_HIGH = (2 | (FTDI_SIO_SET_RTS_MASK << 8));
    const int FTDI_SIO_SET_RTS_LOW = (0 | (FTDI_SIO_SET_RTS_MASK << 8));

    /**
     *  BREAK on/off values obtained from linux driver
     *  https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ftdi_sio.h
     */
    const int FTDI_SIO_SET_BREAK_ON = (1 << 14);
    const int FTDI_SIO_SET_BREAK_OFF = (0 << 14);

    const int FTDI_BAUDRATE_300 = 0x2710;
    const int FTDI_BAUDRATE_600 = 0x1388;
    const int FTDI_BAUDRATE_1200 = 0x09c4;
    const int FTDI_BAUDRATE_2400 = 0x04e2;
    const int FTDI_BAUDRATE_4800 = 0x0271;
    const int FTDI_BAUDRATE_9600 = 0x4138;
    const int FTDI_BAUDRATE_19200 = 0x809c;
    const int FTDI_BAUDRATE_38400 = 0xc04e;
    const int FTDI_BAUDRATE_57600 = 0x0034;
    const int FTDI_BAUDRATE_115200 = 0x001a;
    const int FTDI_BAUDRATE_230400 = 0x000d;
    const int FTDI_BAUDRATE_460800 = 0x4006;
    const int FTDI_BAUDRATE_921600 = 0x8003;

    /***
     *  Default Serial Configuration
     *  Baud rate: 9600
     *  Data bits: 8
     *  Stop bits: 1
     *  Parity: None
     *  Flow Control: Off
     */
    const int FTDI_SET_DATA_DEFAULT = 0x0008;
    const int FTDI_SET_MODEM_CTRL_DEFAULT1 = 0x0101;
    const int FTDI_SET_MODEM_CTRL_DEFAULT2 = 0x0202;
    const int FTDI_SET_MODEM_CTRL_DEFAULT3 = 0x0100;
    const int FTDI_SET_MODEM_CTRL_DEFAULT4 = 0x0200;
    const int FTDI_SET_FLOW_CTRL_DEFAULT = 0x0000;

    int setControlCommand(void *handle, int request, int value, int index)
    {
        int dataLength = 0;
        int response = x_libusb_controltransfer(handle, FTDI_REQTYPE_HOST2DEVICE, request, value, 0 + 1 + index, NULL, 0, 1000);
        return response;
    }

    int  Init(void *handle)
    {
        if(setControlCommand(handle, FTDI_SIO_RESET, 0x00, 0) < 0)
        {
            return -1;
        }
        if(setControlCommand(handle, FTDI_SIO_SET_DATA, FTDI_SET_DATA_DEFAULT, 0) < 0)
        {
            return -1;
        }
        if(setControlCommand(handle, FTDI_SIO_MODEM_CTRL, FTDI_SET_MODEM_CTRL_DEFAULT1, 0) < 0)
        {
            return -1;
        }
        if(setControlCommand(handle, FTDI_SIO_MODEM_CTRL, FTDI_SET_MODEM_CTRL_DEFAULT2, 0) < 0)
        {
            return -1;
        }
        if(setControlCommand(handle, FTDI_SIO_SET_FLOW_CTRL, FTDI_SET_FLOW_CTRL_DEFAULT, 0) < 0)
        {
            return -1;
        }
        if(setControlCommand(handle, FTDI_SIO_SET_BAUD_RATE, FTDI_BAUDRATE_115200, 0) < 0)
        {
            return -1;
        }
        return 0;
    }

    void  Free(void *handle)
    {
        setControlCommand(handle, FTDI_SIO_MODEM_CTRL, FTDI_SET_MODEM_CTRL_DEFAULT3, 0);
        setControlCommand(handle, FTDI_SIO_MODEM_CTRL, FTDI_SET_MODEM_CTRL_DEFAULT4, 0);
    }

    void SetBaudRate(void *handle, int baudRate)
    {
        int value = 0;
        if(baudRate >= 0 && baudRate <= 300)
        {
            value = FTDI_BAUDRATE_300;
        }
        else if(baudRate > 300 && baudRate <= 600)
        {
            value = FTDI_BAUDRATE_600;
        }
        else if(baudRate > 600 && baudRate <= 1200)
        {
            value = FTDI_BAUDRATE_1200;
        }
        else if(baudRate > 1200 && baudRate <= 2400)
        {
            value = FTDI_BAUDRATE_2400;
        }
        else if(baudRate > 2400 && baudRate <= 4800)
        {
            value = FTDI_BAUDRATE_4800;
        }
        else if(baudRate > 4800 && baudRate <= 9600)
        {
            value = FTDI_BAUDRATE_9600;
        }
        else if(baudRate > 9600 && baudRate <= 19200)
        {
            value = FTDI_BAUDRATE_19200;
        }
        else if(baudRate > 19200 && baudRate <= 38400)
        {
            value = FTDI_BAUDRATE_38400;
        }
        else if(baudRate > 19200 && baudRate <= 57600)
        {
            value = FTDI_BAUDRATE_57600;
        }
        else if(baudRate > 57600 && baudRate <= 115200)
        {
            value = FTDI_BAUDRATE_115200;
        }
        else if(baudRate > 115200 && baudRate <= 230400)
        {
            value = FTDI_BAUDRATE_230400;
        }
        else if(baudRate > 230400 && baudRate <= 460800)
        {
            value = FTDI_BAUDRATE_460800;
        }
        else if(baudRate > 460800 && baudRate <= 921600)
        {
            value = FTDI_BAUDRATE_921600;
        }
        else if(baudRate > 921600)
        {
            value = FTDI_BAUDRATE_921600;
        }
        else
        {
            value = FTDI_BAUDRATE_9600;
        }

        setControlCommand(handle, FTDI_SIO_SET_BAUD_RATE, value, 0);
    }

    void SetParity(void *handle, int parity)
    {

    }

    void SetFlowControl(void *handle, int flowControl)
    {

    }

    void SetDataBits(void *handle, int dataBits)
    {

    }

    void SetStopBits(void *handle, int stopBits)
    {

    }

    void SetBreak(void *handle, int state)
    {

    }

};

